package br.unb.midialab.pianogame.midi;

import java.io.File;
import java.io.IOException;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiFileFormat;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;

import br.unb.midialab.pianogame.model.Musica;
import br.unb.midialab.pianogame.model.Nota;

public class MidiParser {
	public static final int NOTE_ON = 0x90;
	public static final int NOTE_OFF = 0x80;
	public static final String[] NOTE_NAMES = { "C", "C#", "D", "D#", "E", "F",
			"F#", "G", "G#", "A", "A#", "B" };

	public static Musica parseMidi(String midiFile) {
		return getListaNota(midiFile);
	}

	public static Musica getListaNota(String midiFileName) {
		int menorNota = 127;
		int maiorNota = 0;
		Musica musica = new Musica();
		Sequence sequence;
		int resolution;
		float bpm;
		int roundBPM;
		float tickTime;
		File midiFile;

		try {
			midiFile = new File("resources/"+ midiFileName);
			sequence = MidiSystem.getSequence(midiFile);
			MidiFileFormat mff = MidiSystem.getMidiFileFormat(midiFile);
			// assuming myMidiFile is the midi file of interest,...
			
			for (String key   : mff.properties().keySet()) {
			     System.out.println(key);  //get() is less efficient 
			} 
//			String title = mff.getProperty("title").toString();
//			musica.setTitulo(title);
//			
//			String author = mff.getProperty("author").toString();
//			musica.setTitulo(author);
			
			
			resolution = sequence.getResolution();
			Sequencer sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.setSequence(sequence);
			bpm = sequencer.getTempoInBPM();
			roundBPM = Math.round(bpm);
			tickTime = 60000f / (resolution * roundBPM);

			int trackNumber = 0;
			
			for (Track track : sequence.getTracks()) {
				trackNumber++;
				long ticks = track.ticks();
				
				for (int i = 0; i < track.size(); i++) {
					MidiEvent event = track.get(i);
					
					MidiMessage message = event.getMessage();
					if (message instanceof ShortMessage) {
						ShortMessage sm = (ShortMessage) message;

						if (sm.getCommand() == NOTE_ON) {
							int key = sm.getData1();
							int octave = (key / 12) - 1;
							int note = key % 12;
							String noteName = NOTE_NAMES[note];
							int velocity = sm.getData2();

							Nota nota = new Nota();

							nota.setMidiKey(key);
							nota.setOctave(octave);
							nota.setNome(noteName);
							nota.setKeyVelocity(velocity);
							nota.setNoteOnTick(event.getTick() * -1);

							for (int j = i + 1; j < track.size(); j++) {
								MidiEvent event2 = track.get(j);
								MidiMessage message2 = event2.getMessage();
								if (message2 instanceof ShortMessage) {
									ShortMessage sm2 = (ShortMessage) message2;
									if (sm2.getData1() == nota.getMidiKey()) {
										nota.setNoteOffTick(event2.getTick()
												* -1);
										break;

									} else if (sm2.getCommand() == NOTE_OFF
											&& sm2.getData1() == nota
													.getMidiKey()) {
										nota.setNoteOffTick(event2.getTick()
												* -1);
										break;
									}
								}
							}
							musica.listaNotas.add(nota);

						}
					}

				}
			}
			musica.setBPM(roundBPM);
			musica.setResolution(resolution);
			musica.setTickTime(tickTime);
			musica.setTickLength(sequence.getTickLength());

			return musica;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		catch (MidiUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		} catch (InvalidMidiDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}

}
